/*
 *
 *
 */
#include <string.h>
#include "gkt_board.h"
#include "gkt_os.h"
#include "gkt_debug.h"
#include "gkt_crc.h"
#include "gkt_flash_partition.h"
#include "gkt_door.h"
#include "window_common.h"
#if	defined(GKT_CONFIG_PROXIMITY_SENSOR_ENABLE)&&(GKT_CONFIG_PROXIMITY_SENSOR_ENABLE)
#include "gkt_pr_sensor.h"
#endif
#if defined(GKT_CONFIG_DOOR_TRIAL_MODE_ENABLE) && GKT_CONFIG_DOOR_TRIAL_MODE_ENABLE
#include "window_trial.h"
#endif
#define WINDOW_DATA_MAGIC	0x312D0BF3

struct _window_data_part_header {
	uint32_t	magic;
	uint32_t	counter;
	uint16_t	crc16;
} __ALIGNED(WINDOW_DATA_ALIGNMENT);
typedef struct _window_data_part_header	window_data_part_header_s;
#define WINDOW_DATA_PART_HEADER_SIZE	sizeof(window_data_part_header_s)

typedef struct {
	window_data_part_header_s	header;
	union {
		struct {
			window_unreset_data_s	unreset;
			window_data_s	normal;
		};
		uint8_t	data[1];
	};
} window_data_part_s;
#define WINDOW_DATA_PART_SIZE	sizeof(window_data_part_s)

#define WINDOW_DATA_PART_NUMS	2
typedef struct {
	const gkt_flash_partition_info_s *c_flash_info;
	const window_data_part_s	*c_part;
	uint32_t	aligned_size;
	uint32_t	current_index;

	window_data_part_s	v_part;
} window_data_instance_s;
static window_data_instance_s s_window_data_instance;

window_unreset_data_s *gp_window_unreset_data = &s_window_data_instance.v_part.unreset;
window_data_s *gp_window_data = &s_window_data_instance.v_part.normal;
__RETMEM_USR window_retmem_data_s g_window_retmem_data;
window_ram_data_s g_window_ram_data;

static void window_data_init_unreset(void)
{
	memset(&s_window_data_instance.v_part.unreset, 0, 
		sizeof(window_unreset_data_s));

#if !GKT_DOOR_LOCK_MOTOR_ROTATE_DIRECTION_FIXED
	s_window_data_instance.v_part.unreset.lock_motor_rotate_direction =
		GKT_DOOR_LOCK_MOTOR_ROTATE_DIRECTION;
#endif
#if !GKT_DOOR_LOCK_MOTOR_ROTATE_TIME_FIXED
	s_window_data_instance.v_part.unreset.lock_motor_rotate_time_ms =
		GKT_DOOR_LOCK_MOTOR_ROTATE_TIME_MS;
#endif
#if defined(GKT_CONFIG_DOOR_LOCK_AUTOMATIC) && GKT_CONFIG_DOOR_LOCK_AUTOMATIC
	s_window_data_instance.v_part.unreset.lock_body_type =
		GKT_DOOR_LOCK_BODY_TYPE_NORMAL;
	s_window_data_instance.v_part.unreset.lock_motor_torsion_gear =
		GKT_DOOR_LOCK_MOTOR_TORSION_GEAR_LOW;
	s_window_data_instance.v_part.unreset.lock_motor_full_rotate_time_ms =
		GKT_DOOR_LOCK_MOTOR_FULL_ROTATE_TIME_MS;
	gp_window_unreset_data->lock_motor_self_learn_complete = 0;
#endif
#if defined(GKT_CONFIG_DOOR_TRIAL_MODE_ENABLE) && GKT_CONFIG_DOOR_TRIAL_MODE_ENABLE
	s_window_data_instance.v_part.unreset.trial_auth_mode = WINDOW_TRIAL_AUTH_MODE_TEMPORARY;
	s_window_data_instance.v_part.unreset.trial_auth_count_max = WINDOW_TRIAL_AUTH_COUNT_MAX;
	s_window_data_instance.v_part.unreset.trial_auth_count_min = WINDOW_TRIAL_AUTH_COUNT_MIN;
	s_window_data_instance.v_part.unreset.trial_auth_counter = 0;
#endif

	gkt_trace("window_data: trial_auth_counter - trial_auth_max_counter, (%d, %d)\n", 
					gp_window_unreset_data->trial_auth_counter, gp_window_unreset_data->trial_auth_max_counter);
}

int window_data_init(void)
{
	const gkt_flash_partition_info_s *c_flash_info;
	uint32_t last_counter, total_size, i;
	uint16_t crc16;
	int retval;

	memset(&g_window_ram_data, 0, sizeof(window_ram_data_s));
	g_window_ram_data.login_admin_user_index = ~0;

	s_window_data_instance.c_flash_info = NULL;
	s_window_data_instance.current_index = WINDOW_DATA_PART_NUMS;

	c_flash_info = gkt_flash_partition_get_info("WindowData");
	if (!c_flash_info) {
		gkt_error("window_data: get flash part failed!\n");
		return GKT_ENOTEXIST;
	}

	s_window_data_instance.aligned_size = 
		gkt_aligned(WINDOW_DATA_PART_SIZE, c_flash_info->sector_size);
	total_size = WINDOW_DATA_PART_NUMS * s_window_data_instance.aligned_size;
	if (c_flash_info->size < total_size) {
		gkt_printf("window_data: UNMATCHED - size(%u @ %u)\n", 
				c_flash_info->size, total_size);
		GKT_ASSERT(0);
		return GKT_EUNMATCHED;
	}

	last_counter = ~0;
	for (i = 0; i < WINDOW_DATA_PART_NUMS; i++) {
		if (c_flash_info->read_map_addr) {
			s_window_data_instance.c_part = (const window_data_part_s *)
				((uint8_t *)c_flash_info->read_map_addr 
					+ (i * s_window_data_instance.aligned_size));
		}
		else {
			retval = gkt_flash_partition_read(c_flash_info->id,
						s_window_data_instance.aligned_size * i,
						WINDOW_DATA_PART_SIZE, &s_window_data_instance.v_part);
			if (retval != GKT_SUCCESS) {
				gkt_warning("window_data: read part-%u failed. retval=%d\n", i, retval);
				continue;
			}

			s_window_data_instance.c_part = 
				(const window_data_part_s *)&s_window_data_instance.v_part;
		}

		if (WINDOW_DATA_MAGIC == s_window_data_instance.c_part->header.magic) {
			crc16 = gkt_crc16_ccitt_false(s_window_data_instance.c_part->data,
							WINDOW_DATA_PART_SIZE - WINDOW_DATA_PART_HEADER_SIZE);
			if (crc16 == s_window_data_instance.c_part->header.crc16) {
				gkt_trace("window_data: (%u) curr_index(%u), counter(%u)\n", i,
							s_window_data_instance.current_index,
							s_window_data_instance.c_part->header.counter);

				if ((s_window_data_instance.current_index >= WINDOW_DATA_PART_NUMS)
					|| (last_counter < s_window_data_instance.c_part->header.counter))
				{
					s_window_data_instance.current_index = i;
					last_counter = s_window_data_instance.c_part->header.counter;
				}
			}
			else {
				gkt_trace("window_data: init - unmatched crc16(0x%04x - 0x%04x) of part %u\n",
						crc16, s_window_data_instance.c_part->header.crc16, i);
			}
		}
		else {
			gkt_trace("window_data: init - invalid magic(0x%x) of part %u\n",
						s_window_data_instance.c_part->header.magic, i);
		}
	}

	s_window_data_instance.c_flash_info = c_flash_info;

	if (s_window_data_instance.current_index < WINDOW_DATA_PART_NUMS) {
		gkt_info("window_data: init - load current(%u)\n", 
						s_window_data_instance.current_index);
		if (c_flash_info->read_map_addr) {
			s_window_data_instance.c_part = (const window_data_part_s *)
				((uint8_t *)c_flash_info->read_map_addr 
					+ (s_window_data_instance.current_index * s_window_data_instance.aligned_size));
			memcpy(&s_window_data_instance.v_part, s_window_data_instance.c_part, 
					WINDOW_DATA_PART_SIZE);
		}
		else if (s_window_data_instance.current_index != (WINDOW_DATA_PART_NUMS - 1)) {
			gkt_flash_partition_read(s_window_data_instance.c_flash_info->id,
					s_window_data_instance.aligned_size * s_window_data_instance.current_index,
					WINDOW_DATA_PART_SIZE, &s_window_data_instance.v_part);
		}
	}
	else {
		window_data_init_unreset();
		window_data_reset();
	}

	return GKT_SUCCESS;
}

static int window_data_save_part(const void *data,
				uint32_t part_index)
{
	uint32_t offset;
	int retval, attemp_times;

	attemp_times = 3;
	offset = s_window_data_instance.aligned_size * part_index;
	do {
		retval = gkt_flash_partition_erase(
						s_window_data_instance.c_flash_info->id,
						offset, s_window_data_instance.aligned_size);
		if (GKT_SUCCESS == retval) {
			retval = gkt_flash_partition_write(
							s_window_data_instance.c_flash_info->id,
							offset, WINDOW_DATA_PART_SIZE, 
							data, 0);
			if (GKT_SUCCESS == retval)
				break;
		}

		gkt_thread_sleep(10);
	} while (--attemp_times > 0);

	return retval;
}

static int window_data_save_inner(void)
{
	uint32_t next_index;
	int retval;

	s_window_data_instance.v_part.header.magic = WINDOW_DATA_MAGIC;
	s_window_data_instance.v_part.header.counter++;
	s_window_data_instance.v_part.header.crc16 = gkt_crc16_ccitt_false(
					s_window_data_instance.v_part.data,
					WINDOW_DATA_PART_SIZE - WINDOW_DATA_PART_HEADER_SIZE);

	if (s_window_data_instance.current_index < WINDOW_DATA_PART_NUMS)
		next_index = (s_window_data_instance.current_index + 1) % WINDOW_DATA_PART_NUMS;
	else
		next_index = 0;

	retval =  window_data_save_part(&s_window_data_instance.v_part, next_index);
	if (GKT_SUCCESS == retval) {
		gkt_info("window_data: save - %u --> %u\n",
					s_window_data_instance.current_index, next_index);
		s_window_data_instance.current_index = next_index;
	}
	else
		gkt_error("window_data: save - failed %d\n", retval);

	return retval;
}

int window_data_save(void)
{
	int retval;
	uint16_t crc16;

	if (s_window_data_instance.c_flash_info) {
		if (s_window_data_instance.current_index < WINDOW_DATA_PART_NUMS) {
			crc16 = gkt_crc16_ccitt_false(s_window_data_instance.v_part.data,
					WINDOW_DATA_PART_SIZE - WINDOW_DATA_PART_HEADER_SIZE);
			if (crc16 == s_window_data_instance.v_part.header.crc16)
				return GKT_SUCCESS;
		}

		retval = window_data_save_inner();
	}
	else
		retval = GKT_ENOTINIT;

	return retval;
}

static void window_data_reset_normal(void)
{
	memset(&s_window_data_instance.v_part.normal, 0, sizeof(window_data_s));

#if defined(GKT_CONFIG_MULTIPLE_LANGUAGE_SUPPORT) && GKT_CONFIG_MULTIPLE_LANGUAGE_SUPPORT
	s_window_data_instance.v_part.normal.language = GKT_LANGUAGE_DEFAULT;
	gkt_language_config(gp_window_data->language);
#endif
#if !GKT_VOICE_OUTPUT_VOLUME_DISABLE
	s_window_data_instance.v_part.normal.voice_volume = GKT_VOICE_OUTPUT_VOLUME_DEFAULT;
	gkt_voice_output_set_volume(gp_window_data->voice_volume);
#endif
#if GKT_CONFIG_PROXIMITY_SENSOR_ENABLE
	gp_window_data->pr_sensor_state = GKT_PR_SENSOR_SENSE_DEFAULT;
#endif

	s_window_data_instance.v_part.normal.lock_auto_close_time = 
		WINDOW_LOCK_AUTO_CLOSE_TIME_DEFAULT;
	s_window_data_instance.v_part.normal.guest_access_time = 
		(WINDOW_GUEST_ACCESS_TIME_START_HOUR_DEFAULT << 24)
		| (WINDOW_GUEST_ACCESS_TIME_START_MINUTE_DEFAULT << 16)
		| (WINDOW_GUEST_ACCESS_TIME_END_HOUR_DEFAULT << 8)
		| WINDOW_GUEST_ACCESS_TIME_END_MINUTE_DEFAULT;
	s_window_data_instance.v_part.normal.door_tamper_alarm_enable =
		WINDOW_DOOR_TAMPER_ALARM_ENABLE_DEFAULT;
}

void window_data_reset(void)
{
	uint32_t index, value;

	gkt_info("window_data: reset ...\n");

	memset(&g_window_retmem_data, 0, sizeof(window_retmem_data_s));
	BPK_ReadKey(&value, 1, GKT_RETMEM_ALWAYS_MODE_STATUS);
	value = 0;
	BPK_WriteKey(&value, 1, GKT_RETMEM_ALWAYS_MODE_STATUS);

	BPK_WriteKey(&g_window_retmem_data.verify_failed_counts, 1, GKT_RETMEM_WINDOW_VERIFY_FAIL_CNT);
	BPK_WriteKey(&g_window_retmem_data.verify_failed_expire_dtu, 1, GKT_RETMEM_WINDOW_VERIFY_EXPIRE_DTU);
	BPK_WriteKey(&g_window_retmem_data.verify_locked_clear_dtu, 1, GKT_RETMEM_WINDOW_VERIFY_LOCK_CLEAR_DTU);
	window_data_reset_normal();

	if (s_window_data_instance.c_flash_info) {
		for (index = 0; index < WINDOW_DATA_PART_NUMS; index++)
			window_data_save_inner();
	}
}

